/*
 * Copyright (C) 2025 Tsingteng MicroSystem
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "nci_data.h"
#include "priority_queue.h"
#include "nci_transport.h"
#include "nci_core.h"
#include "nfc_log_api.h"
#include "nci_task.h"

/**
 * @file nci_data.c
 * @brief 实现 NCI_DATA 模块
*/

#define MAX_DYNMIC_CHANNEL_SIZE 2

static bool s_is_initialized = false;
static tNCI_DATA_CHANNEL s_rf_data_channel = {
    .conn_id = NCI_DATA_CHANNEL_RF,
    .mutex = MUTEX_INIT_VAL("rf_channel"),
};
static tNCI_DATA_CHANNEL s_hci_data_channel = {
    .conn_id = NCI_DATA_CHANNEL_HCI,
    .mutex = MUTEX_INIT_VAL("hci_channel"),
};
static tNCI_DATA_CHANNEL *s_dynmic_channels[MAX_DYNMIC_CHANNEL_SIZE] = {0};
static tMUTEX s_dynmic_channels_lock = MUTEX_INIT_VAL("dynamic_channel");

/**
 * @brief 通过通道号查找动态通道
 *
 * @param conn_id 需要查找的通道号
 *
 * @return 找到符合条件通道时返回通道指针，否则返回NULL
*/
static tNCI_DATA_CHANNEL *find_dynamic_channel(uint8_t conn_id) {
    tNCI_DATA_CHANNEL *target_channel = NULL;

    for (uint8_t i = 0; i < MAX_DYNMIC_CHANNEL_SIZE; i++) {
        if (s_dynmic_channels[i] && s_dynmic_channels[i]->conn_id == conn_id) {
            target_channel = s_dynmic_channels[i];
            break;
        }
    }

    if (!target_channel) {
        NFCLOG_E("channel:%d not found", conn_id);
    }
    return target_channel;
}

/**
 * @brief 打开一个DATA通道
*/
static tNCI_DATA_CHANNEL *open_channel(uint8_t conn_id, uint8_t max_data_packet_payload_size, uint8_t init_credit) {
    if (osi_mutex_lock(&s_dynmic_channels_lock) != NFC_STATUS_OK) {
        NFCLOG_E("lock mutex failed");
        return NULL;
    }
    tNCI_DATA_CHANNEL **pp_channel = NULL;
    tNCI_DATA_CHANNEL *channel = NULL;
    // 查找当前通道是否已经存在
    if (find_dynamic_channel(conn_id)) {
        NFCLOG_E("channel:%d already exists", conn_id);
        goto failed_unlock;
    }
    // 查找是否还有空余的空间创建新通道
    for (uint8_t i = 0; i < MAX_DYNMIC_CHANNEL_SIZE; i++) {
        if (!s_dynmic_channels[i]) {
            channel = osi_calloc(sizeof(tNCI_DATA_CHANNEL));
            s_dynmic_channels[i] = channel;
            pp_channel = &s_dynmic_channels[i];
            break;
        }
    }
    if (!channel) {
        NFCLOG_E("alloc channel failed");
        goto failed_unlock;
    }
    // 初始化通道
    int count = 0;
    if (!osi_snprintf_s(channel->mutex.name, MUTEX_NAME_MAX_LEN, &count, "channel(%d)", conn_id)) {
        NFCLOG_E("snprintf mutex name failed, conn_id=%d, count=%d", conn_id, count);
        goto failed_cleanup;
    }
    if (osi_mutex_init(&channel->mutex) != NFC_STATUS_OK) {
        NFCLOG_E("init mutex failed");
        goto failed_cleanup;
    }
    channel->conn_id = conn_id;
    channel->max_data_packet_payload_size = max_data_packet_payload_size;
    channel->credit = init_credit;
    osi_mutex_unlock(&s_dynmic_channels_lock);
    NFCLOG_I("open channel(%d) success", conn_id);
    return channel;
failed_cleanup:
    if (channel) {
        osi_free(channel);
    }
    if (pp_channel) {
        *pp_channel = NULL;
    }
failed_unlock:
    osi_mutex_unlock(&s_dynmic_channels_lock);
    return NULL;
}

/**
 * @brief 关闭一个DATA通道
*/
static void close_channel(tNCI_DATA_CHANNEL *channel) {
    if (channel == NULL) {
        return;
    }
    if (channel->conn_id == NCI_DATA_CHANNEL_RF || channel->conn_id == NCI_DATA_CHANNEL_HCI) {
        NFCLOG_E("can not close static channel(%d)", channel->conn_id);
        return;
    }
    if (osi_mutex_lock(&s_dynmic_channels_lock) != NFC_STATUS_OK) {
        NFCLOG_E("lock mutex failed");
        return;
    }
    for (uint8_t i = 0; i < MAX_DYNMIC_CHANNEL_SIZE; i++) {
        if (s_dynmic_channels[i] == channel) {
            s_dynmic_channels[i] = NULL;
        }
    }
    if (osi_mutex_deinit(&channel->mutex) != NFC_STATUS_OK) {
        NFCLOG_E("deinit mutex failed");
    }
    osi_free(channel);
    osi_mutex_unlock(&s_dynmic_channels_lock);
}

/**
 * @brief 初始化 NCI_DATA 模块
 *
 * @return 成功返回NCI_STATUS_OK, 失败返回其他错误码
*/
tNCI_STATUS nci_data_init(const tNCI_INIT_RESP *p_init_resp) {
    if (s_is_initialized) {
        nci_data_deinit();
    }
    s_rf_data_channel.max_data_packet_payload_size = 0xFF;
    s_rf_data_channel.credit = 1;
    s_rf_data_channel.wait_buf = NULL;
    s_rf_data_channel.cback = NULL;
    if (osi_mutex_init(&s_rf_data_channel.mutex) != NFC_STATUS_OK) {
        NFCLOG_E("init rf channel mutex failed");
        goto failed;
    }

    s_hci_data_channel.max_data_packet_payload_size = p_init_resp->max_hci_packet_payload_size;
    s_hci_data_channel.credit = p_init_resp->hci_credits_number;
    s_hci_data_channel.wait_buf = NULL;
    s_hci_data_channel.cback = NULL;
    if (osi_mutex_init(&s_hci_data_channel.mutex) != NFC_STATUS_OK) {
        NFCLOG_E("init hci channel mutex failed");
        goto failed;
    }

    if (osi_mutex_init(&s_dynmic_channels_lock) != NFC_STATUS_OK) {
        NFCLOG_E("init mutex failed");
        goto failed;
    }

    osi_memset_s(s_dynmic_channels, MAX_DYNMIC_CHANNEL_SIZE * sizeof(tNCI_DATA_CHANNEL*), 0, MAX_DYNMIC_CHANNEL_SIZE * sizeof(tNCI_DATA_CHANNEL*));

    s_is_initialized = true;
    return NCI_STATUS_OK;
failed:
    nci_data_deinit();
    return NCI_STATUS_FAILED;
}

/**
 * @brief 关闭 NCI_DATA 模块
 *
 * @return 成功返回NCI_STATUS_OK, 失败返回其他错误码
*/
tNCI_STATUS nci_data_deinit() {
    osi_mutex_deinit(&s_rf_data_channel.mutex);
    osi_mutex_deinit(&s_hci_data_channel.mutex);
    s_rf_data_channel.cback = NULL;
    s_rf_data_channel.wait_buf = NULL;
    s_hci_data_channel.cback = NULL;
    s_hci_data_channel.wait_buf = NULL;
    if (osi_mutex_lock(&s_dynmic_channels_lock) != NFC_STATUS_OK) {
        NFCLOG_E("lock mutex failed");
        return NCI_STATUS_EXT_GET_LOCK_FAILED;
    }
    for (uint8_t i = 0; i < MAX_DYNMIC_CHANNEL_SIZE; i++) {
        if (!s_dynmic_channels[i]) {
            continue;
        }
        NFCLOG_I("close channel=%p", s_dynmic_channels[i]);
        osi_mutex_deinit(&s_dynmic_channels[i]->mutex);
        osi_free(s_dynmic_channels[i]);
        s_dynmic_channels[i] = NULL;
    }
    osi_mutex_unlock(&s_dynmic_channels_lock);
    osi_mutex_deinit(&s_dynmic_channels_lock);
    s_is_initialized = false;
    return NCI_STATUS_OK;
}

/**
 * @brief 通过conn_id查找通道
 *
 * @note 只能查找已经打开的通道
 *
 * @param conn_id 通道ID
 * @return 查找成功返回通道, 失败返回NULL
*/
tNCI_DATA_CHANNEL *nci_data_find_channel(uint8_t conn_id) {
    if (!s_is_initialized) {
        return NULL;
    }
    // 静态通道
    if (conn_id == NCI_DATA_CHANNEL_RF) {
        return &s_rf_data_channel;
    } else if (conn_id == NCI_DATA_CHANNEL_HCI) {
        return &s_hci_data_channel;
    }
    if (osi_mutex_lock(&s_dynmic_channels_lock) != NFC_STATUS_OK) {
        NFCLOG_E("lock mutex failed");
        return NULL;
    }
    tNCI_DATA_CHANNEL *channel = find_dynamic_channel(conn_id);
    osi_mutex_unlock(&s_dynmic_channels_lock);
    return channel;
}

/**
 * @brief 设置NCI DATA通道的初始credit和最大负载数据长度
 *
 * @param conn_id 需要设置的通道ID
 * @param mdpps 最大负载数据长度
 * @param init_credit 初始credit
 * @return 成功返回NCI_STATUS_OK, 失败返回错误码
 */
tNCI_STATUS nci_data_set_channel_info(uint8_t conn_id, uint8_t mdpps, uint8_t init_credit) {
    tNCI_DATA_CHANNEL *channel = nci_data_find_channel(conn_id);
    if (!channel) {
        NFCLOG_E("can not find channel(%d)", conn_id);
        return NCI_STATUS_EXT_NOT_FOUND;
    }
    if (osi_mutex_lock(&channel->mutex) != NFC_STATUS_OK) {
        NFCLOG_E("lock mutex failed");
        return NCI_STATUS_EXT_GET_LOCK_FAILED;
    }

    channel->max_data_packet_payload_size = mdpps;
    channel->credit = init_credit;

    osi_mutex_unlock(&channel->mutex);
    return NCI_STATUS_OK;
}

/**
 * @brief 打开RF动态通道
 *
 * @param[in] discovery_id RF设备ID
 * @param[in] rf_protocol RF协议
 * @param[out] out_conn_id 返回通道ID
*/
tNCI_STATUS nci_data_open_rf_dynamic_channel(tNCI_RF_DISCOVERY_ID discovery_id, tNCI_RF_PROTOCOL rf_protocol, uint8_t *out_conn_id) {
    if (!s_is_initialized) {
        return NCI_STATUS_EXT_NOT_INITIALIZED;
    }
    if (out_conn_id == NULL) {
        return NCI_STATUS_EXT_INVALID_PARAM;
    }
    tNCI_DEST_SPEC_PARAM dest_param[] = {
        {.type = 0, .len = 2, .value = (discovery_id << 8) | rf_protocol,}
    };
    tNCI_DATA_CHANNEL_INFO info;
    tNCI_STATUS ret = nci_core_conn_create(REMOTE_NFC_ENDPOINT, dest_param, 1, &info);
    if (ret == NCI_STATUS_OK) {
        tNCI_DATA_CHANNEL *channel = open_channel(info.conn_id, info.max_data_packet_payload_size, info.initial_num_credits);
        if (channel == NULL) {
            ret = NCI_STATUS_FAILED;
            nci_core_conn_close(info.conn_id);
        } else {
            *out_conn_id = info.conn_id;
        }
    }
    return ret;
}

/**
 * @brief 打开NFCEE动态通道
 *
 * @param[in] nfcee_id NFCEE ID
 * @param[in] nfcee_interface NFCEE接口
 * @param[out] out_conn_id 返回通道ID
 *
 * @return 成功返回NCI_STATUS_OK, 失败返回错误码
*/
tNCI_STATUS nci_data_open_nfcee_dynamic_channel(uint8_t nfcee_id, uint8_t nfcee_interface, uint8_t *out_conn_id) {
    if (!s_is_initialized) {
        return NCI_STATUS_EXT_NOT_INITIALIZED;
    }
    if (out_conn_id == NULL) {
        return NCI_STATUS_EXT_INVALID_PARAM;
    }
    tNCI_DEST_SPEC_PARAM dest_param[] = {
        {.type = 1, .len = 2, .value = (nfcee_id << 8) | nfcee_interface,}
    };
    tNCI_DATA_CHANNEL_INFO channel_info;
    tNCI_STATUS ret = nci_core_conn_create(NFCEE, dest_param, 1, &channel_info);
    if (ret == NCI_STATUS_OK) {
        tNCI_DATA_CHANNEL *channel = open_channel(channel_info.conn_id, channel_info.max_data_packet_payload_size, channel_info.initial_num_credits);
        if (channel == NULL) {
            ret = NCI_STATUS_FAILED;
            nci_core_conn_close(channel_info.conn_id);
        } else {
            *out_conn_id = channel_info.conn_id;
        }
    }
    return ret;
}

/**
 * @brief 关闭NCI DATA通道
 *
 * @param conn_id 需要关闭的通道ID
 *
 * @return 关闭成功返回NCI_STATUS_OK，失败返回错误码
*/
tNCI_STATUS nci_data_channel_close(uint8_t conn_id) {
    if (!s_is_initialized) {
        return NCI_STATUS_EXT_NOT_INITIALIZED;
    }
    if (conn_id == NCI_DATA_CHANNEL_RF || conn_id == NCI_DATA_CHANNEL_HCI) {
        NFCLOG_E("can not close RF or HCI channel");
        return NCI_STATUS_EXT_REJECTED;
    }
    tNCI_DATA_CHANNEL *channel = find_dynamic_channel(conn_id);
    if (channel == NULL) {
        return NCI_STATUS_EXT_CHANNEL_NOT_AVAILABLE;
    }
    tNCI_STATUS ret = NCI_STATUS_FAILED;
    osi_mutex_lock(&channel->mutex);
    ret = nci_core_conn_close(channel->conn_id);
    osi_mutex_unlock(&channel->mutex);
    if (ret == NCI_STATUS_OK) {
        close_channel(channel);
    }
    return ret;
}

/**
 * @brief 向指定NCI DATA通道发送数据
 *
 * @param[in] conn_id 通道ID
 * @param[in] cmd 待发送的数据
 * @param[out] rsp 接收数据的容器
 * @param[in] timeout 等待rsp的超时时间
 *
 * @return 发送成功返回NCI_STATUS_OK，失败返回错误码
*/
tNCI_STATUS nci_data_channel_transmit(uint8_t conn_id, tNCI_BUF *cmd, tNCI_BUF *rsp, uint32_t timeout) {
    if (!s_is_initialized) {
        return NCI_STATUS_EXT_NOT_INITIALIZED;
    }
    if (cmd == NULL || rsp == NULL) {
        return NCI_STATUS_EXT_INVALID_PARAM;
    }
    tNCI_DATA_CHANNEL *channel = nci_data_find_channel(conn_id);
    if (channel == NULL) {
        NFCLOG_E("channel not avaliable, %d", conn_id);
        return NCI_STATUS_EXT_CHANNEL_NOT_AVAILABLE;
    }
    if (!osi_memset_s(&cmd->head, sizeof(cmd->head), 0, sizeof(cmd->head))) {
        NFCLOG_E("memset cmd->head failed");
        return NCI_STATUS_EXT_FAILED;
    }
    cmd->head.gid = conn_id;
    tNCI_STATUS ret = nci_tp_send_data(channel, cmd, rsp, timeout);
    return ret;
}

/**
 * @brief 通知NCI DATA通道有数据可用
 *
 * @param conn_id 通道ID
 * @param credits 可用数据量
*/
void nci_data_channel_notify_credit(uint8_t conn_id, uint8_t credits) {
    if (!s_is_initialized) {
        NFCLOG_E("not initialized");
        return;
    }
    tNCI_DATA_CHANNEL *channel = nci_data_find_channel(conn_id);
    if (channel == NULL) {
        NFCLOG_W("notify credit failed, %d", conn_id)
        return;
    }

    if (osi_mutex_lock(&channel->mutex) != NFC_STATUS_OK) {
        NFCLOG_W("lock channel(%d) failed", conn_id);
        return;
    }
    channel->credit += credits;
    NFCLOG_D("channel(%d) credit update=%d", conn_id, channel->credit);
    osi_mutex_notify(&channel->mutex);
    osi_mutex_unlock(&channel->mutex);
}

/**
 * @brief 向指定NCI DATA通道发送数据，异步方式，不等待RSP
 *
 * @param[in] conn_id 通道ID
 * @param[in] data 待发送的数据
 *
 * @return 发送成功返回NCI_STATUS_OK，失败返回错误码
*/
tNCI_STATUS nci_data_channel_transmit_async(uint8_t conn_id, tNCI_BUF *data) {
    if (!s_is_initialized) {
        return NCI_STATUS_EXT_NOT_INITIALIZED;
    }
    if (data == NULL) {
        return NCI_STATUS_EXT_INVALID_PARAM;
    }
    tNCI_DATA_CHANNEL *channel = nci_data_find_channel(conn_id);
    if (channel == NULL) {
        NFCLOG_E("channel not avaliable, %d", conn_id);
        return NCI_STATUS_EXT_CHANNEL_NOT_AVAILABLE;
    }
    if (!osi_memset_s(&data->head, sizeof(data->head), 0, sizeof(data->head))) {
        NFCLOG_E("memset data->head failed");
        return NCI_STATUS_EXT_FAILED;
    }
    data->head.gid = conn_id;
    return nci_tp_send_data_async(channel, data, NCI_DEFAULT_WAIT_TIME);
}

/**
 * @brief 根据conn_id分发NCI DATA通道数据
 *
 * @param conn_id 数据通道ID
 * @param data 接收的数据
 * @param len 数据长度
 */
void nci_data_dispatch(uint8_t conn_id, uint8_t *data, uint16_t len) {
    if (!data || len == 0) {
        NFCLOG_E("data is NULL or len(%d) == 0", len);
        return;
    }
    if (!s_is_initialized) {
        NFCLOG_E("nci_data not initialized");
        return;
    }
    uint8_t *p_copy_data = (uint8_t *) osi_calloc(len);
    if (!osi_memcpy_s(p_copy_data, len, data, len)) {
        NFCLOG_E("memcpy data failed");
        return;
    }
    tNCI_STATUS ret = nci_task_post(NCI_TASK_EVT_DATA_DISPATCH, (uint32_t) conn_id, (uint64_t) len, (void *) p_copy_data);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("post data dispatch task failed, ret=%d", ret);
        osi_free(p_copy_data);
    }
}
